#!/usr/bin/env python3
"""
Compose Claude Code UI Reconstruction Video — v7 KIMI REPAIR.
Fixes:
1. Crossfade transitions (xfade) between scenes — eliminates PPT硬切
2. Subtitle color #888888, opacity 20, font 13, margin 260 — Kimi suggests
3. Hook density increased (13 flash items)
4. Tighter timing: multi_agent 4.0→3.5, final 3.5→3.0
5. Final scene: "reusable pipeline" + "zero screen recording" badges
"""
import json
import os
import subprocess
from datetime import datetime

BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CONFIG_PATH = os.path.join(BASE, "v7_kimi_repair_loop", "scenes_v7", "scenes_v7.json")
CLIPS_DIR = os.path.join(BASE, "05_clips_v7")
VIDEO_DIR = os.path.join(BASE, "06_video")
V7_DIR = os.path.join(BASE, "v7_kimi_repair_loop")
REVIEW_DIR = os.path.join(V7_DIR, "review_package")
FFMPEG = "D:/AI_WORKSPACE/tools/ffmpeg/ffmpeg.exe"
FFPROBE = "D:/AI_WORKSPACE/tools/ffmpeg/ffprobe.exe"
W, H = 1080, 1920
XFADE_DUR = 0.15  # crossfade duration in seconds

os.makedirs(VIDEO_DIR, exist_ok=True)
os.makedirs(REVIEW_DIR, exist_ok=True)


def load_scene_order():
    with open(CONFIG_PATH, "r", encoding="utf-8") as f:
        data = json.load(f)
    order = []
    for s in data.get("scenes", []):
        order.append((s["scene_id"], s["state_type"], s["duration_seconds"]))
    return order


SCENE_ORDER = load_scene_order()
DURATIONS = [d for _, _, d in SCENE_ORDER]
TOTAL_DUR = sum(DURATIONS)
XFADE_COUNT = len(SCENE_ORDER) - 1
FINAL_DUR = TOTAL_DUR - XFADE_COUNT * XFADE_DUR

# === V7 Subtitles — same 5 core + 1 final "reusable" ===
SUBTITLES_V7 = {
    "scene_hook_montage": [
        (500, 1600, "3 agents running")
    ],
    "scene_multi_agent_launch": [
        (600, 1800, "parallel workers")
    ],
    "scene_fail_quality_gate": [
        (800, 2000, "Quality Gate failed")
    ],
    "scene_fix_apply": [
        (800, 2000, "Fix applied")
    ],
    "scene_fix_rerun_pass": [
        (800, 2000, "89/89 passed")
    ],
    "scene_final_status": [
        (800, 1900, "reusable pipeline")
    ],
}


def scene_abs_offset(scene_index):
    """Calculate the absolute start time of a scene accounting for xfade overlaps."""
    # Each xfade before this scene shortens total by XFADE_DUR
    return sum(DURATIONS[:scene_index]) - scene_index * XFADE_DUR


def generate_srt_v7():
    srt_path = os.path.join(VIDEO_DIR, "subtitles_v7.srt")
    entries = []
    n = 1
    for idx, (sid, _, dur) in enumerate(SCENE_ORDER):
        base_offset = scene_abs_offset(idx)
        for sm, em, txt in SUBTITLES_V7.get(sid, []):
            a0 = base_offset + sm / 1000
            a1 = base_offset + min(em, dur * 1000) / 1000
            a0 = min(a0, FINAL_DUR - 0.1)
            a1 = min(a1, FINAL_DUR)

            def fmt(sec):
                ms = int(sec * 1000)
                return "%02d:%02d:%02d,%03d" % (
                    ms // 3600000, (ms % 3600000) // 60000,
                    (ms % 60000) // 1000, ms % 1000
                )

            entries.append("%d\n%s --> %s\n%s\n" % (n, fmt(a0), fmt(a1), txt))
            n += 1

    with open(srt_path, "w", encoding="utf-8") as f:
        f.write("\n".join(entries))
    print("  SRT v7: %d entries (xfade-adjusted)" % (n - 1))
    return srt_path


def build_xfade_filter():
    """Build filter_complex for concat with xfade transitions between all scenes."""
    filters = []
    input_labels = []
    clip_labels = []

    # Input trim + setpts for each clip
    for i, (sid, _, dur) in enumerate(SCENE_ORDER):
        clip_path = os.path.join(CLIPS_DIR, "%s.mp4" % sid).replace("\\", "/")
        label_in = "%d:v" % i
        label_out = "t%d" % i
        input_labels.append(clip_path)
        clip_labels.append(label_out)
        filters.append("[%s]trim=duration=%.3f,setpts=PTS-STARTPTS[%s]" % (label_in, dur, label_out))

    # Chain xfade transitions
    prev = clip_labels[0]
    for i in range(1, len(clip_labels)):
        offset = sum(DURATIONS[:i]) - i * XFADE_DUR
        cur = clip_labels[i]
        next_name = "c%02d" % i if i < len(clip_labels) - 1 else "final"
        xfade_str = "[%s][%s]xfade=transition=fade:duration=%.2f:offset=%.3f[%s]" % (
            prev, cur, XFADE_DUR, offset, next_name)
        # Debug: print first and last xfade filter
        if i == 1 or i == len(clip_labels) - 1:
            print("  xfade[%d/%d]: %s" % (i, len(clip_labels) - 1, xfade_str))
        filters.append(xfade_str)
        prev = next_name  # no brackets — format adds them

    return input_labels, filters


def compose_video():
    # Build xfade filter_complex
    inputs, filter_parts = build_xfade_filter()

    print("\n=== Compose with xfade (%d transitions × %.2fs) ===" % (XFADE_COUNT, XFADE_DUR))
    print("  Original: %.1fs → Final: %.1fs (-%ds)" % (TOTAL_DUR, FINAL_DUR, XFADE_COUNT * XFADE_DUR))

    # Generate subtitle SRT
    srt_path = generate_srt_v7()
    srt_rel = "06_video/subtitles_v7.srt"
    filter_parts.append("[final]subtitles=%s:force_style="
                        "'FontName=Microsoft YaHei,FontSize=13,"
                        "PrimaryColr=&H00888888,"
                        "BorderStyle=4,BackColr=&H20121216,"
                        "Outline=0,Shadow=0,MarginV=260,Alignment=2'[out]" % srt_rel)

    # Build full filter_complex string
    filter_complex = "; ".join(filter_parts)
    print("\n  Filter complex: %d chars, %d filters" % (len(filter_complex), len(filter_parts)))
    print("  Clips: %d | Inputs: %d" % (len(SCENE_ORDER), len(inputs)))
    # Debug: write filter to file
    debug_path = os.path.join(V7_DIR, "filter_debug.txt")
    with open(debug_path, "w", encoding="utf-8") as f:
        f.write(filter_complex)

    # Build ffmpeg args
    cmd = [FFMPEG, "-y", "-hide_banner"]
    for inp in inputs:
        cmd.extend(["-i", inp])
    cmd.extend([
        "-filter_complex", filter_complex,
        "-map", "[out]",
        "-c:v", "libx264", "-preset", "slow", "-crf", "20",
        "-pix_fmt", "yuv420p",
        os.path.join(VIDEO_DIR, "v22_claude_code_ui_rebuild_multi_agent_v7_kimi_repair.mp4")
    ])

    print("\n=== Running ffmpeg xfade compose ===")
    result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace",
                            cwd=BASE)
    if result.returncode != 0:
        print("STDERR (last 3k):")
        print(result.stderr[-3000:])
        raise RuntimeError("ffmpeg xfade compose failed")
    # Print stderr for debugging even on success
    stderr_lines = result.stderr.strip().split('\n')
    info_lines = [l for l in stderr_lines if any(x in l for x in ['frame=', 'fatal', 'error', 'warn', 'Input', 'Output', 'Stream', 'Duration'])]
    if info_lines:
        for l in info_lines[:20]:
            print("  %s" % l.strip())

    # Probe final
    final = os.path.join(VIDEO_DIR, "v22_claude_code_ui_rebuild_multi_agent_v7_kimi_repair.mp4")
    probe = subprocess.run(
        [FFPROBE, "-v", "error", "-show_entries", "format=duration,size",
         "-of", "default=noprint_wrappers=1", final],
        capture_output=True, text=True, encoding="utf-8", errors="replace"
    )
    print("  Final: %s" % probe.stdout.strip())
    return final


def generate_review_pkg(final_video):
    print("\n=== V7 Review Package ===")

    frames_dir = os.path.join(REVIEW_DIR, "frames")
    os.makedirs(frames_dir, exist_ok=True)

    dur_probe = subprocess.run(
        [FFPROBE, "-v", "error", "-show_entries", "format=duration",
         "-of", "default=noprint_wrappers=1", final_video],
        capture_output=True, text=True, encoding="utf-8", errors="replace"
    ).stdout.strip()
    try:
        td = float(dur_probe.split("=")[1])
    except:
        td = FINAL_DUR
    step = td / 7
    moments = [int(step * i) for i in range(1, 7)]

    for t in moments:
        out = os.path.join(frames_dir, "f_%02d_v7.jpg" % t)
        subprocess.run([FFMPEG, "-y", "-ss", str(t), "-i", final_video,
                        "-vframes", "1", "-q:v", "2", out], capture_output=True)

    from PIL import Image, ImageDraw, ImageFont
    cols, rows = 3, 2
    tw, th = 360, 640
    sheet = Image.new("RGB", (cols * tw, rows * th), (18, 18, 22))
    draw = ImageDraw.Draw(sheet)
    try:
        font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 14)
    except:
        font = ImageFont.load_default()
    for i, t in enumerate(moments):
        fp = os.path.join(frames_dir, "f_%02d_v7.jpg" % t)
        x, y = (i % cols) * tw, (i // cols) * th
        try:
            img = Image.open(fp)
            img.thumbnail((tw - 10, th - 30))
            ix = x + (tw - img.width) // 2
            iy = y + 20 + (th - 30 - img.height) // 2
            sheet.paste(img, (ix, iy))
        except:
            pass
        draw.text((x + tw // 2, y + th - 8), "t=%ds" % t, fill=(148, 152, 162), font=font, anchor="mm")
        draw.rectangle([x, y, x + tw - 1, y + th - 1], outline=(42, 42, 48), width=1)
    cs = os.path.join(REVIEW_DIR, "frame_contact_sheet_v7.jpg")
    sheet.save(cs, "JPEG", quality=85)
    print("  Contact sheet: %d KB" % (os.path.getsize(cs) // 1024))

    probe_out = subprocess.run(
        [FFPROBE, "-v", "error", "-show_entries", "format=duration,size,bit_rate",
         "-show_streams", "-of", "default=noprint_wrappers=1", final_video],
        capture_output=True, text=True, encoding="utf-8", errors="replace"
    ).stdout
    probe_path = os.path.join(REVIEW_DIR, "video_probe_v7.md")
    with open(probe_path, "w", encoding="utf-8") as f:
        f.write("# Video Probe — V7 Kimi Repair\n\n```\n%s\n```\n" % probe_out.strip())
        f.write("\n## Subtitles (V7: 6 subs, xfade-adjusted)\n\n")
        f.write("| # | Time | Text |\n|---|------|------|\n")
        n = 1
        for idx, (sid, _, dur) in enumerate(SCENE_ORDER):
            base = scene_abs_offset(idx)
            for sm, em, txt in SUBTITLES_V7.get(sid, []):
                t_start = base + sm / 1000
                t_end = base + min(em, dur * 1000) / 1000
                f.write("| %d | %.1f-%.1fs | %s |\n" % (n, t_start, t_end, txt))
                n += 1
        f.write("\n## Transitions\n")
        f.write("- Crossfade: %d transitions × %.2fs = %.2fs overlap\n" % (XFADE_COUNT, XFADE_DUR, XFADE_COUNT * XFADE_DUR))
        f.write("- Original duration: %.1fs → Final: %.1fs\n" % (TOTAL_DUR, FINAL_DUR))
        f.write("\n**Scene durations**: %s\n" % " + ".join("%.1f" % d for d in DURATIONS))
    print("  Probe: %s" % probe_path)

    # V6 vs V7 comparison
    comp_path = os.path.join(REVIEW_DIR, "v6_vs_v7_comparison.md")
    with open(comp_path, "w", encoding="utf-8") as f:
        f.write("# V6 vs V7 Comparison\n\n")
        f.write("**Generated**: %s\n\n" % datetime.now().isoformat())
        f.write("## V7 Fixes\n\n")
        f.write("| # | Issue | V6 | V7 |\n|---|-------|-----|-----|\n")
        f.write("| 1 | PPT感: 硬切 | Hard cuts between scenes | **xfade 0.15s transitions** |\n")
        f.write("| 2 | 字幕颜色亮 | #999999 | **#888888** |\n")
        f.write("| 3 | 字幕背景透明度 | 30 | **20** |\n")
        f.write("| 4 | 字号 | 14 | **13** |\n")
        f.write("| 5 | 底部间距 | 240 | **260** |\n")
        f.write("| 6 | Hook密度 | 9 flashes | **13 flashes** (+44%) |\n")
        f.write("| 7 | 多Agent运行 | 4.0s | **3.5s** |\n")
        f.write("| 8 | 最终场景 | 3.5s, 3 badges | **3.0s, 4 badges** (+reusable) |\n")
        f.write("| 9 | 总时长 | 36.5s | **~34s** (with xfade) |\n\n")
        f.write("## Changed Files\n\n")
        f.write("| File | Change |\n")
        f.write("|------|--------|\n")
        f.write("| `scenes_v7.json` | Hook +4 flashes, multi_agent 4.0→3.5, final 3.5→3.0 + badges |\n")
        f.write("| `compose_v7.py` | xfade filter_complex, subtitle #888888/20/13/260, 6th sub |\n")
        f.write("| `render_frames_v7.py` | Config → v7 scenes, output → 04_frames_v7 |\n")
        f.write("| `generate_clips_v7.py` | Config → v7 scenes, input/output dirs |\n\n")
        f.write("## Unchanged\n\n")
        f.write("- All 8.5-9.5 score dimensions: multi_agent (9), animation (9), readability (9), FAIL→FIX→PASS (9)\n")
        f.write("- Code font sizes, diff font sizes, color palette, layout\n")
        f.write("- Render engine structure (same render functions, same logic)\n")
    print("  Comparison: %s" % comp_path)

    # Repair log
    repair_path = os.path.join(REVIEW_DIR, "repair_log.md")
    with open(repair_path, "w", encoding="utf-8") as f:
        f.write("# V7 Repair Log\n\n")
        f.write("**Generated**: %s\n\n" % datetime.now().isoformat())
        f.write("## Issues Fixed\n\n")
        f.write("### HIGH: PPT感 — 硬切过渡\n")
        f.write("- Added xfade=0.15s between all 12 scenes (11 transitions)\n")
        f.write("- Eliminates slide-deck hard cuts, makes video fluid\n\n")
        f.write("### MEDIUM: 字幕视觉重量\n")
        f.write("- Color: #999999 → #888888 (Kimi suggestion)\n")
        f.write("- BackColr opacity: 30 → 20 (Kimi suggestion)\n")
        f.write("- FontSize: 14 → 13\n")
        f.write("- MarginV: 240 → 260 (deeper in bottom safety zone)\n\n")
        f.write("### LOW: Hook密度\n")
        f.write("- Flashes: 9 → 13 (+44%)\n")
        f.write("- Added: Read(log.txt), Context compressed 62%, Quality Gate ✓, 89/89 passed\n\n")
        f.write("### MEDIUM: 场景时长\n")
        f.write("- multi_agent_running: 4.0s → 3.5s (tighten longest scene)\n")
        f.write("- final_status: 3.5s → 3.0s (tighten ending)\n\n")
        f.write("### LOW: 最终场景复用价值\n")
        f.write("- Added badges: 'reusable pipeline' (blue), 'zero screen recording' (yellow)\n")
        f.write("- Added 6th subtitle: 'reusable pipeline'\n\n")
        f.write("## Not Changed\n\n")
        f.write("- Multi-agent scenes (9/10)\n")
        f.write("- Animation (9/10)\n")
        f.write("- Code readability (9/10)\n")
        f.write("- FAIL→FIX→PASS narrative (9/10)\n")
        f.write("- Information rhythm (9.5/10)\n")
        f.write("- Render engine architecture\n")
    print("  Repair log: %s" % repair_path)

    disc_path = os.path.join(REVIEW_DIR, "disclaimer.md")
    with open(disc_path, "w", encoding="utf-8") as f:
        f.write("# Disclaimer — Claude Code UI Rebuild V7\n\n")
        f.write("**This video is a simulated reconstruction** of Claude Code's terminal interface ")
        f.write("for demonstration and tutorial purposes. It is not an actual screen recording.\n\n")
        f.write("- All terminal output, agent interactions, and UI elements are procedurally rendered\n")
        f.write("- Based on observed Claude Code visual patterns and behavior\n")
        f.write("- No actual Claude Code API or session data was captured\n")
        f.write("- Used for educational/portfolio purposes only\n\n")
        f.write("*Generated on %s *\n" % datetime.now().isoformat())

    return {
        "contact_sheet": cs,
        "video_probe": probe_path,
        "comparison": comp_path,
        "repair_log": repair_path,
        "disclaimer": disc_path,
    }


def generate_kimi_audit_prompt():
    kimi_dir = os.path.join(V7_DIR, "kimi_audit")
    os.makedirs(kimi_dir, exist_ok=True)
    path = os.path.join(kimi_dir, "kimi_v7_audit_prompt.md")
    with open(path, "w", encoding="utf-8") as f:
        f.write("# Kimi V7 Audit Request\n\n")
        f.write("## V7 Changes (from V6 → V7)\n\n")
        f.write("V6 score: 89/100\n")
        f.write("V6 blocker: subtitle overlay → fixed in V6\n\n")
        f.write("### V7 Fixes\n\n")
        f.write("1. **Crossfade transitions**: 0.15s xfade between all 12 scenes (was hard cuts)\n")
        f.write("2. **Subtitle color**: #999999→#888888 (Kimi V6.1 suggestion)\n")
        f.write("3. **Subtitle opacity**: 30→20 (Kimi V6.1 suggestion)\n")
        f.write("4. **Subtitle font**: 14→13, margin 240→260\n")
        f.write("5. **Hook density**: 9→13 flash items (+44%)\n")
        f.write("6. **multi_agent_running**: 4.0s→3.5s (tighten)\n")
        f.write("7. **final_status**: 3.5s→3.0s, +'reusable pipeline' +'zero screen recording' badges\n")
        f.write("8. **6th subtitle**: 'reusable pipeline' at final scene\n\n")
        f.write("### Preserved Subtitles (6)\n\n")
        sub_lines = []
        for idx, (sid, _, dur) in enumerate(SCENE_ORDER):
            base = scene_abs_offset(idx)
            for sm, em, txt in SUBTITLES_V7.get(sid, []):
                sub_lines.append("| %.1f-%.1fs | %s |" % (base + sm/1000, base + min(em, dur*1000)/1000, txt))
        for l in sub_lines:
            f.write(l + "\n")
        f.write("\n\n### Request\n\n")
        f.write("Please review the V7 video and score 0-100 using the same 10-dimension rubric.\n")
        f.write("Target: 90+ for freeze. If <90, provide specific issues for V8.\n")
    print("  Kimi audit prompt: %s" % path)


def write_iteration_scoreboard(v7_score=None):
    sb_path = os.path.join(V7_DIR, "iteration_scoreboard.json")
    data = {
        "iterations": [
            {"version": "v1", "score": 69, "delta": 0, "frozen": False},
            {"version": "v2", "score": 72, "delta": "+3", "frozen": False},
            {"version": "v3", "score": 81, "delta": "+9", "frozen": False},
            {"version": "v4", "score": 85, "delta": "+4", "frozen": False},
            {"version": "v5", "score": 86, "delta": "+1", "frozen": False},
            {"version": "v6", "score": 89, "delta": "+3", "frozen": True},
        ]
    }
    if v7_score:
        data["iterations"].append({
            "version": "v7",
            "score": v7_score,
            "delta": "+%d" % (v7_score - 89) if v7_score >= 89 else "%d" % (v7_score - 89),
            "frozen": False
        })
    with open(sb_path, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)
    print("  Scoreboard: %s" % sb_path)


def main():
    print("=" * 60)
    print("Claude Code UI Reconstruction — v7 KIMI REPAIR LOOP")
    print("V6: 89/100 → Target: 90+")
    print("=" * 60)
    print("Scenes: %d | Total: %.1fs → %.1fs (xfade: %.2fs × %d)" %
          (len(SCENE_ORDER), TOTAL_DUR, FINAL_DUR, XFADE_DUR, XFADE_COUNT))

    v = compose_video()
    pkg = generate_review_pkg(v)
    generate_kimi_audit_prompt()
    write_iteration_scoreboard()

    print("\n" + "=" * 60)
    print("v7 VIDEO: %s" % v)
    size_kb = os.path.getsize(v) // 1024
    print("SIZE: %d KB" % size_kb)
    print("=" * 60)


if __name__ == "__main__":
    main()
